// axios 配置简单进行配置
import type { AxiosResponse } from "axios";
import type { RequestOptions, Result } from "/#/axios";
import type { AxiosTransform, CreateAxiosOptions } from "./axiosTransform";
import { RequestEnum, ResultEnum, ContentTypeEnum } from "../enums";
import { VAxios } from "./axios";
import { isString } from "/@/utils/is";
import { setObjToUrlParams, deepMerge } from "/@/utils";
import { clone } from "lodash-es";
import { formatRequestDate, joinTimestamp } from "./helper";
import { getAppEnvConfig } from "../../../build/utils/env";

// 获取请求基础地址以及请求前缀
const { VITE_GLOB_API_URL, VITE_GLOB_API_URL_PREFIX } = getAppEnvConfig();

/**
 * 区分多种数据处理方式
 */
const transform: AxiosTransform = {
  /**
   * 请求之前处理配置项钩子
   * @param config
   * @param options
   * @returns
   */
  beforeRequestHook: (config, options) => {
    const {
      apiUrl,
      joinPrefix,
      joinParamsToUrl,
      formatDate,
      joinTime = true,
      urlPrefix
    } = options;
    if (joinPrefix) {
      config.url = `${urlPrefix}${config.url}`; // 请求地址前缀
    }
    if (apiUrl && isString(apiUrl)) {
      config.url = `${apiUrl}${config.url}`;
    }
    const params = config.params || {};
    const data = config.data || false;
    formatDate && data && !isString(data) && formatRequestDate(data); // 格式化请求数据
    // GET 请求
    if (config.method?.toUpperCase() === RequestEnum.GET) {
      if (!isString(params)) {
        config.params = Object.assign(
          params || {},
          joinTimestamp(joinTime, false) // 给 get 请求加上时间戳参数，避免从缓存中拿数据。
        );
      } else {
        config.url = config.url + params + `${joinTimestamp(joinTime, true)}`; // 兼容 restful 风格
        config.params = undefined;
      }
    } else {
      if (!isString(params)) {
        formatDate && formatRequestDate(params);
        if (
          Reflect.has(config, "data") &&
          config.data &&
          (Object.keys(config.data).length > 0 ||
            config.data instanceof FormData)
        ) {
          config.data = data;
          config.params = params;
        } else {
          // 非get请求如果没有提供data，则将 params 视为 data
          config.data = params;
          config.params = undefined;
        }
        if (joinParamsToUrl) {
          config.url = setObjToUrlParams(
            config.url as string,
            Object.assign({}, config.params, config.data)
          );
        }
      } else {
        config.url = config.url + params; // 兼容restful风格
        config.params = undefined;
      }
    }
    return config;
  },
  /**
   *  处理响应数据钩子,如果数据不是预期格式直接抛出错误
   * @param res
   * @param options
   * @returns
   */
  transformResponseHook: (
    res: AxiosResponse<Result>,
    options: RequestOptions
  ) => {
    const { isTransformResponse, isReturnNativeResponse } = options;
    // 是否返回原生响应头 比如需要获取响应头时使用该属性
    if (isReturnNativeResponse) return res;
    // 数据不做处理,直接获取 code，data，message 这些信息
    if (!isTransformResponse) return res.data;
    const { data } = res;
    if (!data) {
      throw new Error("数据错误");
    }
    // 这里 code，result，message 为后台统一的字段，可以在 types.ts 内修改为项目自己的接口返回格式
    const { code, result, message } = data;
    //! 这里逻辑可以根据项目进行修改
    const hasSuccess =
      data && Reflect.has(data, "code") && code === ResultEnum.SUCCESS;
    if (hasSuccess) {
      return result;
    }
    // 下面错误处理, 根据自己项目的实际情况对不同的code执行不同错误消息操作
    let timeoutMsg = "";
    switch (code) {
      case ResultEnum.TIMEOUT:
        timeoutMsg = "401 错误";
        break;
      default:
        if (message) {
          timeoutMsg = message;
        }
    }
    throw new Error(timeoutMsg);
  },
  /**
   * 请求拦截器处理 (可在此处添加 token 等固定参数)
   * @param config
   * @param options
   * @returns
   */
  requestInterceptors: (config, options) => {
    // 请求之前获取 token 可以根据或工具函数获取
    const token = "cpranmo";
    if (token && options?.requestOptions?.withToken !== false) {
      // jwt token
      // options.headers.Authorization = options.authenticationScheme
      //   ? `${options.authenticationScheme} ${token}`
      //   : token;
    }
    return config;
  },
  /**
   * 响应拦截器处理
   * @param res
   * @returns
   */
  responseInterceptors: (res: AxiosResponse<any>) => {
    return res;
  }
};

function createAxios(opt?: Partial<CreateAxiosOptions>) {
  return new VAxios(
    // 深度合并
    deepMerge(
      {
        // 连接超时
        timeout: 10 * 1000,
        // 基础接口地址
        baseURL: VITE_GLOB_API_URL,
        // 头类型设置
        headers: { "Content-Type": ContentTypeEnum.JSON },
        //  认证机会
        authenticationScheme: "",
        // 数据处理方式
        transform: clone(transform),
        // 用户的请求配置项
        requestOptions: {
          // 默认将prefix 添加到 url
          joinPrefix: true,
          // 是否返回原生响应头
          isReturnNativeResponse: false,
          // 需要对返回数据进行处理
          isTransformResponse: true,
          // post请求的时候添加参数到url
          joinParamsToUrl: false,
          // 格式化提交参数时间
          formatDate: true,
          // 消息提示类型
          errorMessageMode: "message",
          // 接口地址
          // apiUrl: VITE_GLOB_API_URL,
          // 接口拼接地址
          urlPrefix: VITE_GLOB_API_URL_PREFIX,
          //  是否加入时间戳
          joinTime: true,
          // 忽略重复请求
          ignoreCancelToken: true,
          // 是否携带 token
          withToken: true,
          // 重试请求
          retryRequest: {
            isOpenRetry: true,
            count: 5,
            waitTime: 100
          }
        }
      },
      opt || {}
    )
  );
}

//? baseURL, apiUrl, urlPrefix  当有值时拼接的路径顺序是  baseURL/apiUrl/urlPrefix

// 默认请求
export const http = createAxios();

console.log(http);

// 其他类型请求
// export const otherHttp = createAxios({
//   requestOptions: {
//     apiUrl: 'xxx',
//     urlPrefix: 'xxx',
//   },
// });
